package com.autumn.scheduler.bean;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import com.autumn.scheduler.job.JobConstants;
import com.autumn.scheduler.quartz.DruidConnectionProvider;
import com.autumn.scheduler.queue.QueueContext;
import com.autumn.util.AutumnContextUtils;
import com.autumn.util.StringUtils;

/**
 * 调度配置扫描
 *
 * @author 杨昌国 2018-03-18 13:50:45
 */
@Configuration
public class AutumnSchedulerConfiguration {

    private final static String QUARTZ_PREFIX = "org.quartz";
    private final static String SPRING_DATA_SOURCE_PREFIX = "spring.datasource";
    private final static String QUARTZ_DATA_SOURCE_PREFIX = "org.quartz.dataSource.autumn.";
    private final static String THREAD_POOL_PREFIX = "org.quartz.threadPool.";
    private final static String SCHEDULER_PREFIX = "org.quartz.scheduler.";
    private final static String JOBSTORE_PREFIX = "org.quartz.jobStore.";

    /**
     * @param env
     * @param dataSourcePrefix
     * @return
     */
    private Map<String, Object> getDataSourceMap(Environment env, String dataSourcePrefix) {
        Map<String, Object> res = new LinkedHashMap<>(
                new RelaxedPropertyResolver(env, dataSourcePrefix).getSubProperties("."));
        return AutumnContextUtils.handlePropertiesName(res);
    }

    private boolean isNotNull(Object value) {
        if (value == null) {
            return false;
        }
        return !StringUtils.isNullOrBlank(value.toString());
    }

    /**
     * 复制或设置数据源
     *
     * @param env
     * @param quartzProperties
     */
    private void copyOrSetDataSource(Environment env, Properties quartzProperties) {
        Map<String, Object> dataSourceMap = getDataSourceMap(env, SPRING_DATA_SOURCE_PREFIX);
        Object value = dataSourceMap.get("driverClassName");
        if (this.isNotNull(value)) {
            quartzProperties.put(QUARTZ_DATA_SOURCE_PREFIX + "driver", value.toString());
        }
        value = dataSourceMap.get("url");
        if (this.isNotNull(value)) {
            quartzProperties.put(QUARTZ_DATA_SOURCE_PREFIX + "URL", value.toString());
        }
        value = dataSourceMap.get("username");
        if (this.isNotNull(value)) {
            quartzProperties.put(QUARTZ_DATA_SOURCE_PREFIX + "user", value.toString());
        }
        value = dataSourceMap.get("password");
        if (this.isNotNull(value)) {
            quartzProperties.put(QUARTZ_DATA_SOURCE_PREFIX + "password", value.toString());
        }
        String maxConnectionKey = QUARTZ_DATA_SOURCE_PREFIX + "maxConnection";
        value = quartzProperties.get(maxConnectionKey);
        if (!this.isNotNull(value)) {
            value = dataSourceMap.get("maxActive");
            if (this.isNotNull(value)) {
                quartzProperties.put(maxConnectionKey, value.toString());
            } else {
                quartzProperties.put(maxConnectionKey, "10");
            }
        }
        quartzProperties.put(QUARTZ_DATA_SOURCE_PREFIX + "connectionProvider.class",
                DruidConnectionProvider.class.getName());
    }

    /**
     * 设置默认值
     *
     * @param quartzProperties
     * @param key
     * @param defaultValue
     */
    private void setDefaultValue(Properties quartzProperties, String key, Object defaultValue) {
        Object value = quartzProperties.get(key);
        if (!this.isNotNull(value)) {
            quartzProperties.put(key, defaultValue);
        }
    }

    /**
     * 设置默认值
     *
     * @param quartzProperties
     */
    private void setDefaultValue(Properties quartzProperties) {
        // 线程池
        this.setDefaultValue(quartzProperties, THREAD_POOL_PREFIX + "class", "org.quartz.simpl.SimpleThreadPool");
        this.setDefaultValue(quartzProperties, THREAD_POOL_PREFIX + "threadCount", "100");
        this.setDefaultValue(quartzProperties, THREAD_POOL_PREFIX + "threadPriority", "5");
        this.setDefaultValue(quartzProperties,
                THREAD_POOL_PREFIX + "threadsInheritContextClassLoaderOfInitializingThread", "true");
        // 调度
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "rmi.export", "false");
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "rmi.proxy", "false");
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "instanceName", JobConstants.AUTUMN_QUARTZ_SCHEDULER);
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "instanceId", "AUTO");
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "wrapJobExecutionInUserTransaction", "false");
        this.setDefaultValue(quartzProperties, SCHEDULER_PREFIX + "batchTriggerAcquisitionMaxCount", "100");
        // 集群
        this.setDefaultValue(quartzProperties, JOBSTORE_PREFIX + "isClustered", "true");
        this.setDefaultValue(quartzProperties, JOBSTORE_PREFIX + "clusterCheckinInterval", "15000");
        this.setDefaultValue(quartzProperties, JOBSTORE_PREFIX + "maxMisfiresToHandleAtATime", "5");
        this.setDefaultValue(quartzProperties, JOBSTORE_PREFIX + "misfireThreshold", "60000");
    }

    /**
     * 属性
     *
     * @param env
     * @return
     * @throws IOException
     */
    @Bean
    public Properties quartzProperties(Environment env) throws IOException {
        Map<String, Object> res = AutumnContextUtils.handlePropertiesName(
                new LinkedHashMap<>((new RelaxedPropertyResolver(env, QUARTZ_PREFIX).getSubProperties("."))));
        Properties result = new Properties();
        for (String key : res.keySet()) {
            Object value = res.get(key);
            if (value != null) {
                result.put(QUARTZ_PREFIX + "." + key.toString(), value.toString());
            }
        }
        result.put(JOBSTORE_PREFIX + "class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        result.put(JOBSTORE_PREFIX + "tablePrefix", "qrtz_");
        result.put(JOBSTORE_PREFIX + "dataSource", "autumn");
        copyOrSetDataSource(env, result);
        setDefaultValue(result);
        return result;
    }

    /**
     * 调度工厂
     *
     * @param context 应用上下文
     * @param env     环境
     * @return
     * @throws IOException
     */
    @Bean(name = JobConstants.AUTUMN_QUARTZ_SCHEDULER)
    @Primary
    public SchedulerFactoryBean schedulerFactoryBean(Environment env) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setQuartzProperties(quartzProperties(env));
        return factory;
    }

    /**
     * 队列上下文(用于释放上下文持有的资源)
     *
     * @return
     */
    @Bean
    public QueueContext queueContext() {
        return new QueueContext();
    }
}
